package gonz.grabber;

import gonz.common.config.Config;
import gonz.common.config.GrabLink;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.openqa.selenium.WebDriver;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Properties;

abstract public class Grabber {
    private static final Logger LOGGER = Logger.getLogger(Grabber.class);

    private static final char[] FOLDER_ILLEGAL_CHARACTERS = {'\n', '\r', '\t', '\0', '\f', '`', '?', '*', '<', '>', '|', '\"', ':'};
    private static final char[] ILLEGAL_CHARACTERS = {'/', '\n', '\r', '\t', '\0', '\f', '`', '?', '*', '\\', '<', '>', '|', '\"', ':'};
    public static final char ILLEGAL_REPLACER = ' ';

    abstract protected List<Item> grabItems(WebDriver webDriver, GrabLink grabLink);

    public void grab(WebDriver webDriver, GrabLink grabLink) {
        List<Item> items = grabItems(webDriver, grabLink);
        int imgsCount = loadImgs(grabLink, items);
        LOGGER.info(grabLink + " has been loaded. " + imgsCount + " Images.");
    }

    public int loadImgs(GrabLink grabLink, List<Item> items) {
        int itemCounter = 0;
        int imgsCounter = 0;
        for (Item item : items) {
            itemCounter++;
            imgsCounter += loadImgs(grabLink, item);
            if (itemCounter % 50 == 0) {
                LOGGER.info(itemCounter + " of  " + items.size() + ". " + (items.size() - itemCounter) + " items left. Continue...");
            }
            //int percent = (100 * itemCounter) / items.size();
//            if (percent % 10 == 0) {
//                LOGGER.info(percent + "% completed. " + itemCounter + " of  " + items.size() + ". " + (items.size() - itemCounter) + " items left. Continue...");
//            }
        }
        return imgsCounter;
    }

    public int loadImgs(GrabLink grabLink, Item item) {
        int imgCounter = 0;
        List<String> imgLinks = item.getImgLinks();
        String imgDstRootFolderName = removeIllegalCharsFromDirName(Config.SETTINGS.getOutputFolderName());
        if (StringUtils.isEmpty(imgDstRootFolderName)) {
            imgDstRootFolderName = "output";
        }
        String imgDstFolderName = imgDstRootFolderName + File.separator + removeIllegalCharsFromDirName(grabLink.getTo());
        File folder = new File(imgDstFolderName);
        folder.mkdirs();
        int curImgLinkNum = 0;
        if (imgLinks == null || imgLinks.size() == 0) {
            LOGGER.error("Item has no images: " + item);
            return 0;
        }
        for (String imgLink : imgLinks) {
            imgCounter++;
            String imgDstFileName = item.getId() + "_" + curImgLinkNum + " " +
                    removeIllegalCharsFromFileName(item.getName()) + "." +
                    StringUtils.substringAfterLast(imgLink, ".");
            curImgLinkNum++;
            String imgDst = imgDstFolderName + File.separator + imgDstFileName;


            OutputStream outputStream = null;
            InputStream inputStream = null;
            try {
                inputStream = openInputStreamToImgLink(imgLink, item);
                outputStream = new BufferedOutputStream(
                        new FileOutputStream(imgDst));


                for (int b; (b = inputStream.read()) != -1; ) {
                    outputStream.write(b);
                }
            } catch (MalformedURLException e) {
                LOGGER.error("Can't load image (" + imgLink + ") for " + item, e);
            } catch (FileNotFoundException e) {
                LOGGER.error("Can't load image (" + imgLink + ") to " + imgDst + " for " + item, e);
            } catch (IOException e) {
                LOGGER.error("Can't load image (" + imgLink + ") to " + imgDst + " for " + item, e);
            } finally {
                try {
                    if (inputStream != null) {
                        inputStream.close();
                    }
                    if (outputStream != null) {
                        outputStream.close();
                    }
                } catch (IOException e) {
                    LOGGER.error("Can't close resources (" + imgLink + ") to " + imgDst + " for " + item, e);
                }
            }
        }
        return imgCounter;
    }

    private InputStream openInputStreamToImgLink(String imgLink, Item item) throws IOException {
        URL imgUrl = new URL(imgLink);
        return handleItemProperties(imgUrl, item);
    }

    private InputStream handleItemProperties(URL imgUrl, Item item) throws IOException {

        Properties itemProperties = item.getProperties();
        String referer = itemProperties.getProperty(ItemProperty.REFERER.toString());
        if (referer != null) {
            URLConnection urlConnection = imgUrl.openConnection();
            urlConnection.addRequestProperty("Referer", referer);
            return urlConnection.getInputStream();
        } else {
            return imgUrl.openStream();
        }
    }

    private String removeIllegalCharsFromDirName(String str) {
        StringUtils.replace(str, "/", File.separator);
        StringUtils.replace(str, "\\", File.separator);

        for (char FOLDER_ILLEGAL_CHARACTER : FOLDER_ILLEGAL_CHARACTERS) {
            str = str.replace(FOLDER_ILLEGAL_CHARACTER, ILLEGAL_REPLACER);
        }

        return str;
    }

    private String removeIllegalCharsFromFileName(String str) {
        StringUtils.replace(str, "/", File.separator);
        StringUtils.replace(str, "\\", File.separator);

        for (char ILLEGAL_CHARACTER : ILLEGAL_CHARACTERS) {
            str = str.replace(ILLEGAL_CHARACTER, ILLEGAL_REPLACER);
        }

        return str;
    }
}
